﻿#include "OiPCH.hpp"

#include <vector>
#include <string>
#include <stdint.h>
#include <omp.h>

using namespace std;

#include <QApplication>
#include <QMainWindow>
#include <QStatusBar>
#include <QToolButton>
#include <QMap>
#include <QImage>
#include <QPainter>
#include <QPicture>
#include <QUuid>
#include <QAction>
#include <QToolBar>
#include <QToolTip>
#include <QListWidget>
#include <QTreeWidget>
#include <QWheelEvent>
#include <QGraphicsProxyWidget>
#include <QMenu>
#include <QTimer>
#include <QRectF>
#include <QMatrix>
#include <QKeyEvent>
#include <QBitmap>
#include <QVBoxLayout>
#include <QOpenGLWidget>

#include "Core/OiSettings.hpp"
#include "View/OiGripper.hpp"
#include "View/OiNotice.hpp"
#include "View/OiPixmap.hpp"
#include "View/OiRaster.hpp"
#include "View/OiRegion.hpp"
#include "View/OiLayer.hpp"
#include "View/OiScene.hpp"
#include "View/OiRuler.hpp"
#include "View/OiTriad.hpp"
#include "Ctrl/OiAxis.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        class ImageViewerContext : public QToolBar
        {
        public:
            ImageViewerContext(QGraphicsScene* scene, QWidget* parent = NULL) : QToolBar(parent)
            {
                this->setAttribute(Qt::WA_TranslucentBackground, true);
            }

            ~ImageViewerContext()
            {
            }

        protected:
            void paintEvent(QPaintEvent* event)
            {
            }
        };

        class ImageViewerViewport : public QOpenGLWidget
        {
        public:
            ImageViewerViewport(ImageViewerImpl* impl, QWidget* parent) : QOpenGLWidget(parent), _impl(impl)
            {
            }

            ~ImageViewerViewport()
            {
            }

        protected:
            void initializeGL();

        public:
            ImageViewerImpl* _impl;
        };

        class ImageViewerImpl : public Impl<ImageViewer>, public QObject
        {
        public:
            void init();
            void exit();

        public:
            bool preview(const QImage& image);
            bool preview(const Core::RangeMapPtr& rangeMap);
            bool preview(const Core::PointMapPtr& pointMap);

        public:
            void relayout(int w = -1, int h = -1);

        public:
            void snapshot(const QString& type = QString());

        public:
            void fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode);

        public:
            void pointMapZToMat();

            QImage normalizedToImage();

        public:
            void updateImage();

        public:
            void onMenuAboutToShow();
            void onMenuAboutToHide();

        public:
            void updateAxis();

        public:
            void display3D_Pointmap();

        public:
            int _mode;
            ImageViewerContext* _tContext;
            QGraphicsProxyWidget* _tContextProxy;

            View::Layer* _dynamicLayer;
            View::Layer* _3DLayer;
            View::Layer* _2DLayer;

            Ctrl::Axis* _hAxis;
            QGraphicsProxyWidget* _hAxisProxy;

            Ctrl::Axis* _vAxis;
            QGraphicsProxyWidget* _vAxisProxy;

            View::Pixmap* _pixmap;
            View::Raster* _raster;

            View::Scene* _scene;

            Core::RangeMapPtr _rangeMap;
            Core::PointMapPtr _pointMap;

            cv::Mat _pointMat;
            QVector<float> _pointZAvg;

            QTimer _timer;
            QTimer _pulse;

            bool _editable;

        public:
            bool _allowHide = true;
            bool _allowShow = true;

        public:
            QAction* _fitAction;
            QAction* _saveAction;
            QAction* _settingsAction;
            QAction* _displayAction;
            QAction* _colorAction;

        public:
            QAction* _saveMAT;
            QAction* _saveXYZ;
            QAction* _saveASC;
            QAction* _saveSTL;
            QAction* _savePLY;
            QAction* _saveIGS;

        public:
            QPointF _rubberBeg;
            QPointF _rubberEnd;
            QRect _rubberRect;

        public:
            ImageViewerViewport* _viewport;

        public:
            QTransform _pixmapTransform;

        public:
            bool _blockContextMenu;
            bool _colorMode;
        };

        void ImageViewerViewport::initializeGL()
        {
            if(_impl->_raster)
            {
                _impl->_raster->initialize();
            }
        }

        void ImageViewerImpl::init()
        {
            OI_Q(ImageViewer);

            _mode = 0;

            _blockContextMenu = false;

            _colorMode = true;

            _editable = true;

            _viewport = new ImageViewerViewport(this, q);

            q->setViewport(_viewport);

            q->setRenderHints(q->renderHints());
            q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
            q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
            q->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
            q->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
            q->setDragMode(QGraphicsView::RubberBandDrag);
            q->setMouseTracking(true);
            q->setFrameShape(QFrame::NoFrame);
            q->setBackgroundBrush(PLScannerStyle::ImageViewerBgColor());
            q->setAttribute(Qt::WA_MacNormalSize);

            _scene = new View::Scene(q);

            q->setScene(_scene);

            _2DLayer = new View::Layer();
            _2DLayer->setFlag(QGraphicsItem::ItemIsFocusable, false);
            _scene->addItem(_2DLayer);
            _2DLayer->setZValue(1);
            _pixmap = new View::Pixmap(_2DLayer);

            _pixmapTransform.rotate(-90);
            _pixmap->setTransform(_pixmapTransform);

            _3DLayer = new View::Layer();
            _3DLayer->setFlag(QGraphicsItem::ItemIsFocusable, false);
            _scene->addItem(_3DLayer);
            _3DLayer->setZValue(200);

            _raster = new View::Raster(_viewport, _3DLayer);

            _dynamicLayer = new View::Layer();
            _dynamicLayer->setFlag(QGraphicsItem::ItemIsFocusable, false);
            _dynamicLayer->setParentItem(_2DLayer);

            _tContext = new ImageViewerContext(_scene);
            _tContext->setAttribute(Qt::WA_GroupLeader);
            _tContext->setOrientation(Qt::Horizontal);
            _tContext->setIconSize(QSize(32, 32));
            _tContextProxy = _scene->addWidget(_tContext);
            _tContextProxy->setFlag(QGraphicsItem::ItemIsFocusable, false);
            _tContextProxy->setZValue(300);

            _vAxis = new Ctrl::Axis();
            _vAxis->setMode(Ctrl::Axis::vertical);
            _vAxis->setType(Ctrl::Axis::rangeType);
            _vAxis->setAttribute(Qt::WA_GroupLeader);
            _vAxisProxy = _scene->addWidget(_vAxis);
            _vAxisProxy->setFlag(QGraphicsItem::ItemIsFocusable, false);
            _vAxisProxy->setZValue(300);
            _vAxis->setStyleSheet("font-size:6pt;background-color:#a7a7a7");

            _hAxis = new Ctrl::Axis();
            _hAxis->setMode(Ctrl::Axis::horiztal);
            _hAxis->setType(Ctrl::Axis::rangeType);
            _hAxis->setAttribute(Qt::WA_GroupLeader);
            _hAxisProxy = _scene->addWidget(_hAxis);
            _hAxisProxy->setFlag(QGraphicsItem::ItemIsFocusable, false);
            _hAxisProxy->setZValue(300);
            _hAxis->setStyleSheet("font-size:6pt;background-color:#a7a7a7");

            _colorAction = _tContext->addAction(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/mono.png"), ImageViewer::tr("黑白图"), q, SLOT(onActionTriggered()));
            _colorAction->setData(5);

            _displayAction = _tContext->addAction(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/3d.png"), ImageViewer::tr("三维模式"), q, SLOT(onActionTriggered()));
            _displayAction->setData(3);

            _fitAction = _tContext->addAction(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/zoomFit_light.png"), ImageViewer::tr("缩放还原"), q, SLOT(onActionTriggered()));
            _fitAction->setData(0);

            _2DLayer->show();
            _3DLayer->hide();

            _scene->setSceneRect(1.0 * INT_MIN, 1.0 * INT_MIN, 2.0 * INT_MAX, 2.0 * INT_MAX);

            _timer.setSingleShot(true);
            _timer.setInterval(5000);
            _pulse.setInterval(50);

            QVector<QRgb> userColorMap;
            for(int i = 0; i < PLScanner::instance()->zColoredMap.size(); i++)
            {
               userColorMap.push_back(PLScanner::instance()->zColoredMap[i].rgb());
            }

            for(int i = PLScanner::instance()->zColoredMap.size(); i < 256; i++)
            {
                userColorMap.push_back(PLScanner::instance()->zColorBg.rgb());
            }

            Core::OiSetUserColorMap(userColorMap);

            q->connect(&_timer, SIGNAL(timeout()), SLOT(onTimeout()));
            q->connect(&_pulse, SIGNAL(timeout()), SLOT(invalidate()));
        }

        void ImageViewerImpl::exit()
        {
            _viewport->makeCurrent();

            if(_raster)
            {
                delete _raster;
            }

            if(_dynamicLayer)
            {
                for(auto layer : _dynamicLayer->childItems())
                {
                    _scene->removeItem(layer);
                }
            }

            if(_scene)
            {
                delete _scene;
            }

             _viewport->doneCurrent();
        }

        void ImageViewerImpl::relayout(int w, int h)
        {
            OI_Q(ImageViewer);

            if(w == -1 || h == -1)
            {
                w = q->viewport()->width();
                h = q->viewport()->height();
            }

            _hAxis->resize(QSize(w, 30));
            _vAxis->resize(QSize(32, h));
            _tContext->resize(_tContext->sizeHint());

            QPointF tPos = q->mapToScene((w - _tContext->sizeHint().width()) / 2, 0);
            QPointF hPos = q->mapToScene(0, h - 30);
            QPointF vPos = q->mapToScene(0, 0);

            _tContextProxy->setPos(tPos);
            _hAxisProxy->setPos(hPos);
            _vAxisProxy->setPos(vPos);

            QTransform proxyTransform;
            proxyTransform.scale(1.0 / q->transform().m11(), 1.0 / q->transform().m22());

            _tContextProxy->setTransform(proxyTransform);
            _hAxisProxy->setTransform(proxyTransform);
            _vAxisProxy->setTransform(proxyTransform);

        }

        void ImageViewerImpl::snapshot(const QString& type)
        {
            OI_Q(ImageViewer);

            auto imageDir = Core::OiGetVariableDir(ImageDir);

            auto imageRnd = Core::OiSubString(QUuid::createUuid().toString().toUpper(), 1, -1);

            int lod = Core::OiSettings()->value("LOD").value<int>();

            if(type.isEmpty() || type == "MAT")
            {
                if(_3DLayer->isVisible() && !_rangeMap.isNull())
                {
                    _rangeMap->save(imageDir + "\\" + imageRnd + ".mat");
                }
                else
                {
                    if(!_pixmap->pixmap().isNull())
                    {
                        _pixmap->pixmap().save(imageDir + "\\" + imageRnd + ".bmp");
                    }
                }
            }
            else if(type == "XYZ")
            {
                Core::PointMapPtr pointMap;
                
                if(!_pointMap.isNull())
                {
                    pointMap = _pointMap;
                }
                else
                {
                    return;
                }
               

                if(!pointMap && !pointMap->isValid())
                {
                    return;
                }

                QString fileName = imageRnd + ".xyz";
                QString fileDir = imageDir + "\\" + fileName;
                QFile file(fileDir);
                if(!file.open(QFile::WriteOnly))
                {
                    return;
                }

                QTextStream stream(&file);

                for(int i = 0; i < pointMap->count(); i++)
                {
                    const float* x = pointMap->xLine(i);
                    const float* y = pointMap->yLine(i);
                    const float* z = pointMap->zLine(i);

                    for(int j = 0; j < pointMap->cols(); j++)
                    {
                        if(!Math::IsNan(x[j]))
                        {
                            stream << x[j] << " " << y[j] << " " << z[j] << "\n";
                        }
                    }
                }
            }
            else if(type == "ASC")
            {
                Core::PointMapPtr pointMap;
               
                if(!_pointMap.isNull())
                {
                    pointMap = _pointMap;
                }
                else
                {
                    return;
                }
                

                if(!pointMap && !pointMap->isValid())
                {
                    return;
                }

                QString fileName = imageRnd + ".asc";
                QString fileDir = imageDir + "\\" + fileName;
                QFile file(fileDir);
                if(!file.open(QFile::WriteOnly))
                {
                    return;
                }

                QTextStream stream(&file);

                for(int i = 0; i < pointMap->count(); i++)
                {
                    const float* x = pointMap->xLine(i);
                    const float* y = pointMap->yLine(i);
                    const float* z = pointMap->zLine(i);

                    for(int j = 0; j < pointMap->cols(); j++)
                    {
                        if(!Math::IsNan(x[j]))
                        {
                            stream << x[j] << " " << y[j] << " " << z[j] << "\n";
                        }
                    }
                }
            }
            else if(type == "STL")//binary stl(stlb)
            {
                Core::RangeMapPtr rangeMap;
                bool rangeValid = false;
               
                if(_3DLayer->isVisible() && !_rangeMap.isNull())
                {
                    rangeMap = _rangeMap;
                    rangeValid = true;
                }
               

                if(rangeValid && rangeMap && rangeMap->isValid())
                {
                    QString fileName = imageRnd + ".stl";
                    QString fileDir = imageDir + "\\" + fileName;
                    QFile file(fileDir);
                    if(!file.open(QFile::WriteOnly))
                    {
                        return;
                    }

                    QDataStream stream(&file);
                    stream.setByteOrder(QDataStream::LittleEndian);
                    stream.setFloatingPointPrecision(QDataStream::SinglePrecision);

                    uint8_t header[80] = { 0 };
                    stream.writeRawData((char*)&header[0], sizeof(header));

                    uint32_t facetNum = 0;
                    for(int row = 0; row < ( rangeMap->rows() - 1 ); row++)
                    {
                        float* rowp = rangeMap->profile(row);
                        float* rown = rangeMap->profile(row + 1);
                        for(int col = 0; col < ( rangeMap->cols() - 1 ); col++)
                        {
                            if(rowp[col] == 0 || rown[col + 1] == 0)
                            {
                                continue;
                            }

                            if(rowp[col + 1] != 0)
                            {
                                facetNum++;
                            }

                            if(rown[col] != 0)
                            {
                                facetNum++;
                            }
                        }
                    }
                    stream.writeRawData((char*)&facetNum, sizeof(facetNum));

                    uint16_t attribute = 0;
                    float facet[12] = { 0.0f };
                    facet[0] = 0.0f;
                    facet[1] = 0.0f;
                    facet[2] = 1.0f;
                    for(int row = 0; row < ( rangeMap->rows() - lod ); row++)
                    {
                        float* rowp = rangeMap->profile(row);
                        float* rown = rangeMap->profile(row + lod);
                        for(int col = 0; col < ( rangeMap->cols() - lod ); col++)
                        {
                            if(rowp[col] == 0 || rown[col + lod] == 0)
                            {
                                continue;
                            }

                            if(rowp[col + lod] != 0)
                            {
                                facet[3] = col;
                                facet[4] = row;
                                facet[5] = rowp[col];

                                facet[6] = col + lod;
                                facet[7] = row;
                                facet[8] = rowp[col + lod];

                                facet[9] = col + lod;
                                facet[10] = row + lod;
                                facet[11] = rown[col + lod];
                                for(int i = 0; i < 12; i++)
                                {
                                    stream.writeRawData((char*)&facet[i], 4);
                                }
                                stream.writeRawData((char*)&attribute, sizeof(attribute));
                            }

                            if(rown[col] != 0)
                            {
                                facet[3] = col;
                                facet[4] = row;
                                facet[5] = rowp[col];

                                facet[6] = col + lod;
                                facet[7] = row + lod;
                                facet[8] = rown[col + lod];

                                facet[9] = col;
                                facet[10] = row + lod;
                                facet[11] = rown[col];
                                for(int i = 0; i < 12; i++)
                                {
                                    stream.writeRawData((char*)&facet[i], 4);
                                }
                                stream.writeRawData((char*)&attribute, sizeof(attribute));
                            }
                        }
                    }
                }

                Core::PointMapPtr pointMap;
                bool pointValid = false;
                
                if(_3DLayer->isVisible() && !_pointMap.isNull())
                {
                    pointMap = _pointMap;
                    pointValid = true;
                }
                

                if(pointValid && pointMap && pointMap->isValid())
                {
                    QString fileName = imageRnd + ".stl";
                    QString fileDir = imageDir + "\\" + fileName;
                    QFile file(fileDir);
                    if(!file.open(QFile::WriteOnly))
                    {
                        return;
                    }

                    QDataStream stream(&file);
                    stream.setByteOrder(QDataStream::LittleEndian);
                    stream.setFloatingPointPrecision(QDataStream::SinglePrecision);

                    uint8_t header[80] = { 0 };
                    stream.writeRawData((char*)&header[0], sizeof(header));

                    uint32_t facetNum = 0;
                    for(int row = 0; row < ( pointMap->rows() - lod ); row++)
                    {
                        float* xlinep = pointMap->xLine(row);
                        float* ylinep = pointMap->yLine(row);
                        float* zlinep = pointMap->zLine(row);
                        float* xlinen = pointMap->xLine(row + lod);
                        float* ylinen = pointMap->yLine(row + lod);
                        float* zlinen = pointMap->zLine(row + lod);
                        for(int col = 0; col < ( pointMap->cols() - lod ); col++)
                        {
                            if(Math::IsNan(xlinep[col]) || Math::IsNan(xlinen[col + lod]) || Math::IsNan(ylinep[col]) || Math::IsNan(ylinen[col + lod])
                                || Math::IsNan(zlinep[col]) || Math::IsNan(zlinen[col + lod]))
                            {
                                continue;
                            }

                            if(!Math::IsNan(xlinep[col + lod]) && !Math::IsNan(ylinep[col + lod]) && !Math::IsNan(zlinep[col + lod]))
                            {
                                facetNum++;
                            }

                            if(!Math::IsNan(xlinen[col]) && !Math::IsNan(ylinen[col]) && !Math::IsNan(zlinen[col]))
                            {
                                facetNum++;
                            }
                        }
                    }
                    stream.writeRawData((char*)&facetNum, sizeof(facetNum));

                    uint16_t attribute = 0;
                    float facet[12] = { 0.0f };
                    facet[0] = 0.0f;
                    facet[1] = 0.0f;
                    facet[2] = 1.0f;
                    for(int row = 0; row < ( pointMap->rows() - lod ); row++)
                    {
                        float* xlinep = pointMap->xLine(row);
                        float* ylinep = pointMap->yLine(row);
                        float* zlinep = pointMap->zLine(row);
                        float* xlinen = pointMap->xLine(row + lod);
                        float* ylinen = pointMap->yLine(row + lod);
                        float* zlinen = pointMap->zLine(row + lod);
                        for(int col = 0; col < ( pointMap->cols() - lod ); col++)
                        {
                            if(Math::IsNan(xlinep[col]) || Math::IsNan(xlinen[col + lod]) || Math::IsNan(ylinep[col]) || Math::IsNan(ylinen[col + lod])
                                || Math::IsNan(zlinep[col]) || Math::IsNan(zlinen[col + lod]))
                            {
                                continue;
                            }

                            if(!Math::IsNan(xlinep[col + lod]) && !Math::IsNan(ylinep[col + lod]) && !Math::IsNan(zlinep[col + lod]))
                            {
                                facet[3] = xlinep[col];
                                facet[4] = ylinep[col];
                                facet[5] = zlinep[col];

                                facet[6] = xlinep[col + lod];
                                facet[7] = ylinep[col + lod];
                                facet[8] = zlinep[col + lod];

                                facet[9] = xlinen[col + lod];
                                facet[10] = ylinen[col + lod];
                                facet[11] = zlinen[col + lod];
                                for(int i = 0; i < 12; i++)
                                {
                                    stream.writeRawData((char*)&facet[i], 4);
                                }
                                stream.writeRawData((char*)&attribute, sizeof(attribute));
                            }

                            if(!Math::IsNan(xlinen[col]) && !Math::IsNan(ylinen[col]) && !Math::IsNan(zlinen[col]))
                            {
                                facet[3] = xlinep[col];
                                facet[4] = ylinep[col];
                                facet[5] = zlinep[col];

                                facet[6] = xlinen[col];
                                facet[7] = ylinen[col];
                                facet[8] = zlinen[col];

                                facet[9] = xlinen[col + lod];
                                facet[10] = ylinen[col + lod];
                                facet[11] = zlinen[col + lod];
                                for(int i = 0; i < 12; i++)
                                {
                                    stream.writeRawData((char*)&facet[i], 4);
                                }
                                stream.writeRawData((char*)&attribute, sizeof(attribute));
                            }
                        }
                    }
                }
            }
            else if(type == "PLY")//binary ply
            {
                Core::RangeMapPtr rangeMap;
                
                if(_3DLayer->isVisible() && !_rangeMap.isNull())
                {
                    rangeMap = _rangeMap;
                }
                else
                {
                    return;
                }
                
                if(!rangeMap && !rangeMap->isValid())
                {
                    return;
                }

                QString fileName = imageRnd + ".ply";
                QString fileDir = imageDir + "\\" + fileName;
                QFile file(fileDir);
                if(!file.open(QFile::WriteOnly))
                {
                    return;
                }

                QDataStream stream(&file);
                stream.setByteOrder(QDataStream::LittleEndian);
                stream.setFloatingPointPrecision(QDataStream::SinglePrecision);

                int cols = rangeMap->cols();
                int rows = rangeMap->rows();
                QByteArray header = QByteArray("ply\n");
                for(int i = 0; i < header.size(); i++)
                {
                    uint8_t temp = (uint8_t)header[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray format = QByteArray("format binary_little_endian 1.0\n");
                for(int i = 0; i < format.size(); i++)
                {
                    uint8_t temp = (uint8_t)format[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray vertexSize = QByteArray(QString("element vertex %1\n").arg(cols * rows).toLatin1());
                for(int i = 0; i < vertexSize.size(); i++)
                {
                    uint8_t temp = (uint8_t)vertexSize[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray propertyx = QByteArray("property float x\n");
                for(int i = 0; i < propertyx.size(); i++)
                {
                    uint8_t temp = (uint8_t)propertyx[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray propertyy = QByteArray("property float y\n");
                for(int i = 0; i < propertyy.size(); i++)
                {
                    uint8_t temp = (uint8_t)propertyy[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray propertyz = QByteArray("property float z\n");
                for(int i = 0; i < propertyz.size(); i++)
                {
                    uint8_t temp = (uint8_t)propertyz[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }

                int faceCount = 0;
                for(int row = 1; row < rows; row += 2)
                {
                    float* rowd = rangeMap->profile(row);
                    for(int col = 1; col < cols; col += 2)
                    {
                        if(rowd[col] != 0)
                        {
                            faceCount++;
                        }
                    }
                }

                QByteArray faceSize = QByteArray(QString("element face %1\n").arg(faceCount).toLatin1());
                for(int i = 0; i < faceSize.size(); i++)
                {
                    uint8_t temp = (uint8_t)faceSize[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray vertexIndexType = QByteArray("property list uint uint vertex_indices\n");
                for(int i = 0; i < vertexIndexType.size(); i++)
                {
                    uint8_t temp = (uint8_t)vertexIndexType[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }
                QByteArray endHeader = QByteArray("end_header\n");
                for(int i = 0; i < endHeader.size(); i++)
                {
                    uint8_t temp = (uint8_t)endHeader[i];
                    stream.writeRawData((char*)&temp, sizeof(uint8_t));
                }

                for(int row = 0; row < rows; row++)
                {
                    float* rowd = rangeMap->profile(row);
                    for(int col = 0; col < cols; col++)
                    {
                        float colf = (float)col;
                        float rowf = (float)row;
                        stream.writeRawData((char*)&colf, sizeof(float));
                        stream.writeRawData((char*)&rowf, sizeof(float));
                        stream.writeRawData((char*)&rowd[col], sizeof(float));
                    }
                }

                for(int row = 1; row < rows; row += 2)
                {
                    float* rowp = rangeMap->profile(row - 1);
                    float* rowc = rangeMap->profile(row);
                    float* rown = nullptr;
                    if(row != rows - 1)
                    {
                        rown = rangeMap->profile(row + 1);
                    }

                    for(int col = 1; col < cols; col += 2)
                    {
                        if(rowc[col] == 0)
                        {
                            continue;
                        }

                        std::vector<uint> entries;
                        entries.push_back(row * cols + col);

                        int firstInd = row * cols + col;
                        bool initfirst = false;

                        if(rown != nullptr)
                        {
                            if(rown[col] != 0)
                            {
                                entries.push_back(( row + 1 ) * cols + col);
                                if(!initfirst)
                                {
                                    firstInd = ( row + 1 ) * cols + col;
                                    initfirst = true;
                                }
                            }

                            if(rown[col - 1] != 0)
                            {
                                entries.push_back(( row + 1 ) * cols + col - 1);
                                if(!initfirst)
                                {
                                    firstInd = ( row + 1 ) * cols + col - 1;
                                    initfirst = true;
                                }
                            }
                        }

                        if(rowc[col - 1] != 0)
                        {
                            entries.push_back(row * cols + col - 1);
                            if(!initfirst)
                            {
                                firstInd = row * cols + col - 1;
                                initfirst = true;
                            }
                        }

                        if(rowp[col - 1] != 0)
                        {
                            entries.push_back(( row - 1 ) * cols + col - 1);
                            if(!initfirst)
                            {
                                firstInd = ( row - 1 ) * cols + col - 1;
                                initfirst = true;
                            }
                        }

                        if(rowp[col] != 0)
                        {
                            entries.push_back(( row - 1 ) * cols + col);
                            if(!initfirst)
                            {
                                firstInd = ( row - 1 ) * cols + col;
                                initfirst = true;
                            }
                        }

                        if(col != cols - 1)
                        {
                            if(rowp[col + 1] != 0)
                            {
                                entries.push_back(( row - 1 ) * cols + col + 1);
                                if(!initfirst)
                                {
                                    firstInd = ( row - 1 ) * cols + col + 1;
                                    initfirst = true;
                                }
                            }

                            if(rowc[col + 1] != 0)
                            {
                                entries.push_back(row * cols + col + 1);
                                if(!initfirst)
                                {
                                    firstInd = row * cols + col + 1;
                                    initfirst = true;
                                }
                            }

                            if(rown != nullptr && rown[col + 1] != 0)
                            {
                                entries.push_back(( row + 1 ) * cols + col + 1);
                                if(!initfirst)
                                {
                                    firstInd = ( row + 1 ) * cols + col + 1;
                                    initfirst = true;
                                }
                            }
                        }

                        entries.push_back(firstInd);

                        uint entriesSize = entries.size();
                        stream.writeRawData((char*)&entriesSize, sizeof(uint));
                        for(int i = 0; i < entries.size(); i++)
                        {
                            stream.writeRawData((char*)&entries[i], sizeof(uint));
                        }
                    }
                }
            }
            else if(type == "IGS")
            {
                Core::PointMapPtr pointMap;

                if(!_pointMap.isNull())
                {
                    pointMap = _pointMap;
                }
                else
                {
                    return;
                }
               

                if(!pointMap && !pointMap->isValid())
                {
                    return;
                }

                if(pointMap->cols() * pointMap->rows() / lod / lod > pow(10, 7))
                {
                    OiWarning() << "Points size overflow! Please down-sampling";
                    OiAlerter() << "Points size overflow! Please down-sampling";
                    return;
                }

                QString fileName = imageRnd + ".igs";
                QString fileDir = imageDir + "\\" + fileName;
                QFile file(fileDir);
                if(!file.open(QFile::WriteOnly))
                {
                    return;
                }

                QByteArray start(80, ' ');
                start[72] = 'S';
                start[79] = '1';
                file.write(start + "\r");

                QByteArray global(80, ' ');
                global.replace(0, 8, "6Hby zdz");
                global[72] = 'G';
                global[79] = '1';
                file.write(global + "\r");

                QByteArray directoryEntry1(80, ' ');
                directoryEntry1.replace(0, 8, QString("%1").arg("116", 8, ' ').toLocal8Bit());//type point
                directoryEntry1.replace(16, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());
                directoryEntry1.replace(24, 8, QString("%1").arg("1", 8, ' ').toLocal8Bit());
                directoryEntry1.replace(32, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());
                directoryEntry1.replace(40, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());
                directoryEntry1.replace(48, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());
                directoryEntry1.replace(56, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());
                directoryEntry1.replace(64, 8, QString("%1").arg("1", 8, ' ').toLocal8Bit());
                QByteArray directoryEntry2(80, ' ');
                directoryEntry2.replace(0, 8, QString("%1").arg("116", 8, ' ').toLocal8Bit());//type
                directoryEntry2.replace(8, 8, QString("%1").arg("1", 8, ' ').toLocal8Bit());
                directoryEntry2.replace(16, 8, QString("%1").arg("3", 8, ' ').toLocal8Bit());//color
                directoryEntry2.replace(24, 8, QString("%1").arg("1", 8, ' ').toLocal8Bit());
                directoryEntry2.replace(32, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());//form Num
                directoryEntry2.replace(64, 8, QString("%1").arg("0", 8, ' ').toLocal8Bit());
                int countDPoint = 1;
                for(int row = 0; row < pointMap->rows() - lod + 1; row += lod)
                {
                    float* xline = pointMap->xLine(row);
                    float* yline = pointMap->yLine(row);
                    float* zline = pointMap->zLine(row);
                    for(int col = 0; col < pointMap->cols() - lod + 1; col += lod)
                    {
                        if(Math::IsNan(xline[col]) || Math::IsNan(yline[col]) || Math::IsNan(zline[col]))
                        {
                            continue;
                        }

                        directoryEntry1.replace(8, 8, QString("%1").arg(QString::number(countDPoint), 8, ' ').toLocal8Bit());//parameterDataNum
                        directoryEntry1.replace(73, 7, QString("%1").arg(QString::number(countDPoint * 2 - 1), 7, ' ').toLocal8Bit());
                        directoryEntry1.replace(72, 1, QString("D").toLocal8Bit());
                        file.write(directoryEntry1 + "\r");

                        directoryEntry2.replace(73, 7, QString("%1").arg(QString::number(countDPoint * 2), 7, ' ').toLocal8Bit());
                        directoryEntry2.replace(72, 1, QString("D").toLocal8Bit());
                        file.write(directoryEntry2 + "\r");
                        countDPoint++;
                    }
                }

                QByteArray parameterData(80, ' ');
                int countPPoint = 1;
                for(int row = 0; row < pointMap->rows() - lod + 1; row += lod)
                {
                    float* xline = pointMap->xLine(row);
                    float* yline = pointMap->yLine(row);
                    float* zline = pointMap->zLine(row);
                    for(int col = 0; col < pointMap->cols() - lod + 1; col += lod)
                    {
                        if(Math::IsNan(xline[col]) || Math::IsNan(yline[col]) || Math::IsNan(zline[col]))
                        {
                            continue;
                        }

                        parameterData.replace(0, 64, QString("%1").arg(QString("116,%1,%2,%3;").arg(xline[col]).arg(yline[col]).arg(zline[col]), -64, ' ').toLocal8Bit());
                        parameterData.replace(64, 8, QString("%1").arg(QString::number(countPPoint * 2 - 1), 8, ' ').toLocal8Bit());
                        parameterData.replace(73, 7, QString("%1").arg(QString::number(countPPoint), 7, ' ').toLocal8Bit());
                        parameterData.replace(72, 1, QString("P").toLocal8Bit());
                        file.write(parameterData + "\r");
                        countPPoint++;
                    }
                }

                QByteArray teminate(80, ' ');
                teminate.replace(0, 8, "S      1");
                teminate.replace(8, 8, "G      1");
                teminate.replace(17, 7, QString("%1").arg(QString::number(countPPoint * 2), 7, ' ').toLocal8Bit());
                teminate.replace(16, 1, "D");
                teminate.replace(25, 7, QString("%1").arg(QString::number(countPPoint), 7, ' ').toLocal8Bit());
                teminate.replace(24, 1, "P");
                teminate.replace(72, 8, QString("T      1").toLocal8Bit());
                file.write(teminate + "\r");

                file.close();
            }
        }

        bool ImageViewerImpl::preview(const QImage& image)
        {
            if(!image.isNull() && image.text("Order") == "BGR")
            {
                _pixmap->setPixmap(image.rgbSwapped());
            }
            else
            {
                _pixmap->setPixmap(image);
            }

            if(!image.isNull())
            {
                if(OI_SELF()->testAttribute(Qt::WA_MacNormalSize))
                {
                    OI_SELF()->zoomFit();
                }
            }

            _rangeMap.clear();

            _pointMap.clear();

            return true;
        }

        bool ImageViewerImpl::preview(const Core::RangeMapPtr& rangeMap)
        {

            auto image = Core::Image::create(Core::Image::MAT, rangeMap->matrix())->to<QImage>(Core::Image::IMG);

            //image.setColorTable(fakeT.table);

            if(!image.isNull() && image.text("Order") == "BGR")
            {
                _pixmap->setPixmap(image.rgbSwapped());
            }
            else
            {
                _pixmap->setPixmap(image);
            }

            _pixmap->colorize(OiFake);

            if(!image.isNull())
            {
                if(OI_SELF()->testAttribute(Qt::WA_MacNormalSize))
                {
                    OI_SELF()->zoomFit();
                }
            }

            _rangeMap = rangeMap;

            _pointMap.clear();
            _pointMat.release();
            _pointZAvg.clear();

            return true;
        }

        bool ImageViewerImpl::preview(const Core::PointMapPtr& pointMap)
        {
            _pointMap = pointMap;

            pointMapZToMat();

            QImage image = normalizedToImage();

            if(!image.isNull() && image.text("Order") == "BGR")
            {
                _pixmap->setPixmap(image.rgbSwapped());
            }
            else
            {
                _pixmap->setPixmap(image);
            }

            if (_colorMode)
            {
                _pixmap->colorize(OiUser);
            }
            else
            {
                _pixmap->colorize(OiGray);
            }

            if(!image.isNull())
            {
                OI_SELF()->zoomFit();
            }

            _rangeMap.clear();

            return true;
        }

        void ImageViewerImpl::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode)
        {
            OI_Q(ImageViewer);

            // Reset the view scale to 1:1.
            QRectF unity = q->matrix().mapRect(QRectF(0, 0, 1, 1));

            if(unity.isEmpty())
            {
                return;
            }

            q->scale(1 / unity.width(), 1 / unity.height());

            int margin = 2;

            QRectF viewRect = q->viewport()->rect().adjusted(margin + (_vAxis->isVisible() ? _vAxis->width() : 0), margin, -margin, -margin - (_hAxis->isVisible() ? _hAxis->height() : 0));

            if(viewRect.isEmpty())
            {
                return;
            }

            QPainterPath path = item->isClipped() ? item->clipPath() : item->shape();

            //////////////////////////////////////////////////////////////////////////
            //     if (item->d_ptr->hasTranslateOnlySceneTransform()) {
            //         path.translate(item->d_ptr->sceneTransform.dx(), item->d_ptr->sceneTransform.dy());
            //         fitInView(path.boundingRect(), aspectRatioMode);
            //     } else {
            //         fitInView(item->d_ptr->sceneTransform.map(path).boundingRect(), aspectRatioMode);
            //     }
            //////////////////////////////////////////////////////////////////////////
            //     path.translate(item->sceneTransform().dx(),item->sceneTransform().dy());
            //     QRectF rect = path.boundingRect();

            QRectF rect = item->sceneTransform().map(path).boundingRect();
            QRectF sceneRect = q->matrix().mapRect(rect);
            if(sceneRect.isEmpty())
            {
                return;
            }

            qreal xratio = viewRect.width() / sceneRect.width();
            qreal yratio = viewRect.height() / sceneRect.height();

            auto offset = QPointF(0, 0);

            // Respect the aspect ratio mode.
            switch(aspectRatioMode)
            {
            case Qt::KeepAspectRatio:
            xratio = yratio = qMin(xratio, yratio);
            q->scale(xratio, yratio);
            offset.setX(-0.5 * (_vAxis->isVisible() ? _vAxis->width() : 0) / xratio);
            offset.setY(0.5 * (_hAxis->isVisible() ? _hAxis->height() : 0) / yratio);
            q->centerOn(rect.center() + offset);
            break;
            case Qt::KeepAspectRatioByExpanding:
            q->scale(xratio, xratio);
            q->centerOn(rect.center().x(), viewRect.center().y() / xratio);
            break;
            case Qt::IgnoreAspectRatio:
            q->scale(xratio, yratio);
            offset.setX(-0.5 * ( _vAxis->isVisible() ? _vAxis->width() : 0 ) / xratio);
            offset.setY(0.5 * ( _hAxis->isVisible() ? _hAxis->height() : 0 ) / yratio);
            q->centerOn(rect.center() + offset);
            break;
            }

            q->setAttribute(Qt::WA_MacNormalSize, false);

            relayout();
        }

        void ImageViewerImpl::pointMapZToMat()
        {
            _pointMat.release();

            if(!_pointMap.isNull() && _pointMap->isValid())
            {
                auto cols = ( PLScanner::instance()->widthMax - PLScanner::instance()->widthMin ) / PLScanner::instance()->widthResolution + 1;
                _pointMat = cv::Mat(_pointMap->count(), cols, CV_32FC1, cv::Scalar(std::numeric_limits<float>::quiet_NaN()));

                cv::Mat lenMat(_pointMap->count(), cols, CV_32FC1, cv::Scalar(2.0 * PLScanner::instance()->widthResolution));
                
                _pointZAvg.resize(cols);

                auto thread_num = 2 * omp_get_num_procs() - 1;

                omp_set_num_threads(thread_num); 

                QMap<int, QVector<float>> pointZSums;
                QMap<int, QVector<int>> pointZCounts;

                for (int i = 0; i < thread_num; i++)
                {
                    for (int j = 0; j < cols; j++)
                    {
                        pointZSums[i] << 0.0;
                        pointZCounts[i] << 0;
                    }
                }

            #pragma omp parallel 
                {
                    #pragma omp for 
                    for(int i = 0; i < _pointMat.rows; i++)
                    {
                        int tid = omp_get_thread_num();
                        auto ppm = (float*)_pointMat.ptr(i);
                        auto px = _pointMap->xLine(i);
                        auto pz = _pointMap->zLine(i);
                        auto len = (float*)lenMat.ptr(i);

                        for(int j = 0; j < _pointMap->cols(); j++)
                        {
                            if(isnan(px[j]) || isnan(pz[j]) )
                            {
                                continue;
                            }

                            double xv = px[j] * 1e3;
                            double zv = pz[j] * 1e3;

                            auto index = ( xv - PLScanner::instance()->widthMin ) / PLScanner::instance()->widthResolution;

                            int li = qFloor(index);
                            int hi = qCeil(index);
                            int ri = qRound(index);

                            if(li >= 0 && li < cols && fabs(index - li) < len[li])
                            {
                                len[li] = fabs(index - li);
                                ppm[li] = zv;
                            }

                            if(hi >= 0 && hi < cols && fabs(index - hi) < len[hi])
                            {
                                len[hi] = fabs(index - hi);
                                ppm[hi] = zv;
                            }

                            if (ri >= 0 && ri < cols)
                            {
                                pointZSums[tid][ri] += zv;
                                pointZCounts[tid][ri]++;

                            }
                        }
                    }
                }

                for (int j = 0; j < cols; j++)
                {
                    float sum = 0.0;
                    int count = 0;
                    for (int i = 0; i < thread_num; i++)
                    {
                        sum += pointZSums[i][j];
                        count += pointZCounts[i][j];
                    }

                    _pointZAvg[j] = sum / count;
                }

                cv::Mat avgMat(1, _pointZAvg.size(), CV_32FC1, _pointZAvg.data());
                cv::medianBlur(avgMat, avgMat, 5);
            }
        }

        QImage ImageViewerImpl::normalizedToImage()
        {
            if(_pointMat.empty())
            {
                return QImage();
            }

            if (!_colorMode)
            {
                float maxZ = -99999.0;
                float minZ = +99999.0;

                QList<int> startLen;
                QList<int> endLen;

                int zeroCount = 0;
                for(int i = 0; i < _pointZAvg.size(); i++)
                {
                    if(isnan(_pointZAvg[i]) || _pointZAvg[i] > +1000.0 || _pointZAvg[i] < -1000.0)
                    {
                        zeroCount++;
                        continue;
                    }

                    if (startLen.isEmpty() || zeroCount > 3)
                    {
                        startLen.push_back(i);
                        endLen.push_back(i);
                    }

                    zeroCount = 0;
                    endLen.last() = i;
                }

                int maxLenIndex = 0;
                int maxLen = 0;

                for (int i = 0; i < startLen.size(); i++)
                {
                    if (endLen[i] - startLen[i] > maxLen)
                    {
                        maxLen = endLen[i] - startLen[i];
                        maxLenIndex = i;
                    }
                }

                for (int i = startLen[maxLenIndex]; i <= endLen[maxLenIndex]; i++)
                {
                    auto pz = _pointZAvg[i];

                    if(isnan(pz) || pz > +1000.0 || pz < -1000.0)
                    {
                        continue;
                    }

                    if (maxZ < pz)
                    {
                        maxZ = pz;
                    }

                    if (minZ > pz)
                    {
                        minZ = pz;
                    }
                }

                float minG = 0.0;
                float maxG = 255.0;

                float ratio = ( maxG - minG ) / ( maxZ - minZ );

                QImage image = QImage(_pointMat.cols, _pointMat.rows, QImage::Format_Indexed8);
                omp_set_num_threads(2 * omp_get_num_procs() - 1);

                auto data = image.bits();

            #pragma omp parallel 
                {
                #pragma omp for 
                    for(int i = 0; i < _pointMat.rows; i++)
                    {
                        auto linePtr = data + i * image.bytesPerLine();
                        auto matPtr = (float*)_pointMat.ptr(i);
                        for(int j = 0; j < _pointMat.cols; j++)
                        {
                            if(isnan(matPtr[j]) || matPtr[j] > 1000.0 || matPtr[j] < -1000.0)
                            {
                                linePtr[j] = 0;
                            }
                            else
                            {
                                auto value =  (matPtr[j] - minZ) * ratio + minG ;
                                if (value > 255)
                                {
                                    linePtr[j] = 255;
                                }
                                else if(value < 0)
                                {
                                    linePtr[j] = 0;
                                }
                                else
                                {
                                    linePtr[j] = value;
                                }
                            }
                        }
                    }
                }

                return image;
            }

            QImage image = QImage(_pointMat.cols, _pointMat.rows, QImage::Format_Indexed8);
            omp_set_num_threads(2 * omp_get_num_procs() - 1);

            auto data = image.bits();
            int offsetIndex =  PLScanner::instance()->zColoredMap.count() / 2;
            double resolution = PLScanner::instance()->zColoredMap.count() / PLScanner::instance()->zColoredRange;

        #pragma omp parallel 
            {
            #pragma omp for 
                for(int i = 0; i < _pointMat.rows; i++)
                {
                    auto linePtr = data + i * image.bytesPerLine();
                    auto matPtr = (float*)_pointMat.ptr(i);
                    for(int j = 0; j < _pointMat.cols; j++)
                    {
                        auto avg = _pointZAvg[j];

                        if (isnan(avg))
                        {
                            linePtr[j] = 255;
                        }
                        else if(isnan(matPtr[j]) || matPtr[j] > 1000.0 || matPtr[j] < -1000.0)
                        {
                            linePtr[j] = 255;
                        }
                        else if ( matPtr[j] - avg >= PLScanner::instance()->zColoredRange / 2 )
                        {
                            linePtr[j] = PLScanner::instance()->zColoredMap.count() - 1;
                        }
                        else if (matPtr[j] - avg <= -PLScanner::instance()->zColoredRange / 2)
                        {
                            linePtr[j] = 0;
                        }
                        else
                        {
                            linePtr[j] = qRound(( matPtr[j] - avg ) *  resolution) + offsetIndex;
                        }
                    }
                }
            }

            return image;
        }

        void ImageViewerImpl::updateImage()
        {
            if (_pointMap)
            {
                QImage image = normalizedToImage();

                if(!image.isNull() && image.text("Order") == "BGR")
                {
                    _pixmap->setPixmap(image.rgbSwapped());
                }
                else
                {
                    _pixmap->setPixmap(image);
                }

                if(_colorMode)
                {
                    _pixmap->colorize(OiUser);
                }
                else
                {
                    _pixmap->colorize(OiGray);
                }
            }
        }

        void ImageViewerImpl::onMenuAboutToShow()
        {
            _allowHide = false;
        }

        void ImageViewerImpl::onMenuAboutToHide()
        {
            _allowHide = true;
        }

        void ImageViewerImpl::updateAxis()
        {
            OI_Q(ImageViewer);

            double y_ratio = 1.0;
            if (!_pointMap.isNull() && _pointMap->isValid())
            {
                y_ratio = PLScanner::instance()->widthResolution;
            }

            QPointF tl = q->mapToScene(0, 0);
            QPointF br = q->mapToScene(q->viewport()->width(), q->viewport()->height());

            double cy = fabs(tl.y() + br.y()) / 2;

            if (!_pixmap->pixmap().isNull())
            {
                cy = -_pixmap->mapToScene(0.0, _pixmap->pixmap().width() / 2).x();
            }

            int step = qFloor(fabs( tl.y() - br.y() ) * y_ratio / 100) * 10;
            if(step == 0) step = 1;
            _vAxis->setStep(step);
            _vAxis->setStart((cy  - br.y()) * y_ratio);
            _vAxis->setEnd((cy - tl.y()) * y_ratio);
            _vAxis->setDecimals(0);
            _vAxis->update();

            double x_ratio = 1.0;
            if(!_pointMap.isNull() && _pointMap->isValid() && _pointMap->count() > 0)
            {
                x_ratio = 360.0 / _pointMap->count();
            }

            step = qFloor(fabs(tl.x() - br.x()) * x_ratio / 100) * 10;
            if(step == 0) step = 1;
            _hAxis->setStep(step);
            _hAxis->setStart(tl.x() * x_ratio);
            _hAxis->setEnd(br.x() * x_ratio);
            _hAxis->setDecimals(0);
            _hAxis->update();

            emit q->axisHasChanged();
        }

        void ImageViewerImpl::display3D_Pointmap()
        {
            if (!_pointMap)
            {
                return;
            }

            if (_pointZAvg.empty())
            {
                return;
            }

            QImage cImg = _pixmap->pixmap();

            if (cImg.isNull())
            {
                return;
            }

            auto zm = Core::PointMap::create(cImg.width(), cImg.height());
            zm->resize(cImg.height());

            {
                for(int i = 0; i < cImg.height(); i++)
                {
                    auto pzx = zm->xLine(i);
                    auto pzy = zm->yLine(i);
                    auto pzz = zm->zLine(i);

                    auto pzr = zm->rLine(i);
                    auto pzg = zm->gLine(i);
                    auto pzb = zm->bLine(i);

                    auto radian = 2 * i * M_PI/ cImg.height();

                    auto cosR = qCos(radian);

                    auto sinR = qSin(radian);

                    auto cid = cImg.bits() + i * cImg.bytesPerLine();

                    if (_colorMode)
                    {
                        for(int j = 0; j < cImg.width(); j++)
                        {
                            if(cid[j] >= 0 && cid[j] < PLScanner::instance()->zColoredMap.count())
                            {
                                auto radius = PLScanner::instance()->hubRadius + _pointZAvg[j];

                                pzx[j] = ( PLScanner::instance()->widthResolution * j + PLScanner::instance()->widthMin ) / 1e3;
                                pzy[j] = radius * cosR / 1e3;
                                pzz[j] = radius * sinR / 1e3;

                                auto color = PLScanner::instance()->zColoredMap[cid[j]];

                                pzr[j] = 1.0* color.red() / 255.0;
                                pzg[j] = 1.0* color.green() / 255.0;
                                pzb[j] = 1.0* color.blue() / 255.0;

                            }
                            else
                            {
                                pzx[j] = std::nan("");
                                pzy[j] = std::nan("");
                                pzz[j] = std::nan("");

                                pzr[j] = std::nan("");
                                pzg[j] = std::nan("");
                                pzb[j] = std::nan("");
                            }
                        }
                    }
                    else
                    {
                        for(int j = 0; j < cImg.width(); j++)
                        {
                            if(cid[j] > 0)
                            {
                                auto radius = PLScanner::instance()->hubRadius + _pointZAvg[j];

                                pzx[j] = ( PLScanner::instance()->widthResolution * j + PLScanner::instance()->widthMin ) / 1e3;
                                pzy[j] = radius * cosR / 1e3;
                                pzz[j] = radius * sinR / 1e3;

                                pzr[j] = 1.0* cid[j] / 255.0;
                                pzg[j] = 1.0* cid[j] / 255.0;
                                pzb[j] = 1.0* cid[j] / 255.0;

                            }
                            else
                            {
                                pzx[j] = std::nan("");
                                pzy[j] = std::nan("");
                                pzz[j] = std::nan("");

                                pzr[j] = std::nan("");
                                pzg[j] = std::nan("");
                                pzb[j] = std::nan("");
                            }
                        }
                    }
                }
            }
            _raster->display(zm);
            _raster->display(View::Raster::Luminance);
        }

        ImageViewer::ImageViewer(QWidget* parent) : QGraphicsView(parent)
        {
            OI_I(ImageViewer)->init();
        }

        ImageViewer::~ImageViewer()
        {
            OI_E(ImageViewer)->exit();
        }

        void ImageViewer::zoomOut(double factor)
        {
            OI_F(ImageViewer);

            qreal factorX = transform().m11();
            qreal factorY = transform().m22();
            scale(1.0 / factorX, 1.0 / factorY);

            d->relayout();
            d->updateAxis();
        }

        void ImageViewer::zoomFit()
        {
            OI_F(ImageViewer);

            if(d->_3DLayer->isVisible())
            {
                if(d->_raster)
                {
                    d->fitInView(d->_raster, Qt::KeepAspectRatio);
                    d->_raster->display(View::Raster::IsoView);
                    return;
                }
            }
           
            auto items = scene()->selectedItems();

            if(!items.empty())
            {
                d->fitInView(items.front(), Qt::IgnoreAspectRatio);
            }
            else
            {
                if(d->_pixmap)
                {
                    d->fitInView(d->_pixmap, Qt::IgnoreAspectRatio);
                }
            }

            d->updateAxis();
        }

        void ImageViewer::zoomOrg()
        {
            OI_F(ImageViewer);

            qreal factorX = transform().m11();
            qreal factorY = transform().m22();
            scale(1.0 / factorX, 1.0 / factorY);
            centerOn(viewport()->width() / 2.0, viewport()->height() / 2.0);

            d->relayout();
            d->updateAxis();
        }

        void ImageViewer::showParameterPanel()
        {
            OI_F(ImageViewer);
            if(d->_raster != nullptr)
            {
                d->_raster->showParameterPanel();
                if(d->_raster->isVisible())
                {
                    d->_pulse.start();
                }
            }
        }

        void ImageViewer::hideParameterPanel()
        {
            OI_F(ImageViewer);
            if(d->_raster != nullptr)
            {
                if(d->_raster->isVisible())
                {
                    d->_pulse.stop();
                }
                d->_raster->hideParameterPanel();
            }
        }

        View::Raster* ImageViewer::raster() const
        {
            return OI_IMPL()->_raster;
        }

        View::Pixmap* ImageViewer::pixmap() const
        {
            return OI_IMPL()->_pixmap;
        }

        void ImageViewer::setEditable(bool enable)
        {
            OI_IMPL()->_editable = enable;
        }

        bool ImageViewer::isEditable() const
        {
            return OI_IMPL()->_editable;
        }

        Core::PointMapPtr ImageViewer::pointMap() const
        {
            return OI_IMPL()->_pointMap;
        }

        Core::RangeMapPtr ImageViewer::rangeMap() const
        {
            return OI_IMPL()->_rangeMap;
        }

        cv::Mat ImageViewer::pointRemapMat() const
        {
            return OI_IMPL()->_pointMat;
        }

        QVector<float> ImageViewer::sampleZValues() const
        {
            return OI_IMPL()->_pointZAvg;
        }

        QAction* ImageViewer::colorModeAction() const
        {
            return OI_IMPL()->_colorAction;
        }

        QAction* ImageViewer::displayModeAction() const
        {
            return OI_IMPL()->_displayAction;
        }

        QAction* ImageViewer::fitZoomAction() const
        {
            return OI_IMPL()->_fitAction;
        }

        View::Layer* ImageViewer::dynamicLayer() const
        {
            return OI_IMPL()->_dynamicLayer;
        }

        OiProfiles ImageViewer::profiles() const
        {
            OI_C(ImageViewer);
            if (d->_pointMap.isNull())
            {
                return OiProfiles();
            }

            if (d->_pointMap->count() <= 100)
            {
                return ProfileView::toProfile(d->_pointMap);
            }

            else
            {
                int cols = d->_pointMap->cols();
                int rows = 100;

                QSharedPointer<Core::PointMap> pointMap(new Core::PointMap(cols, rows));

                pointMap->resize(rows);

                memcpy(pointMap->x(), d->_pointMap->x(), rows * cols * sizeof(float));

                memcpy(pointMap->y(), d->_pointMap->y(), rows * cols * sizeof(float));

                memcpy(pointMap->z(), d->_pointMap->z(), rows * cols * sizeof(float));
           
                return ProfileView::toProfile(pointMap);
            }
        }

        bool ImageViewer::isDisplay3D() const
        {
            return OI_IMPL()->_3DLayer->isVisible();
        }

        QRectF ImageViewer::rulerRect() const
        {
            OI_C(ImageViewer);

            QPointF rtl = QPointF(d->_hAxis->start(), d->_vAxis->end());
            QPointF rbr = QPointF(d->_hAxis->end(), d->_vAxis->start());

            return QRectF(rtl, rbr);
        }

        QRectF ImageViewer::imageRect() const
        {
            OI_C(ImageViewer);
            QPointF itl = d->_pixmap->mapToScene(d->_pixmap->boundingRect().topLeft());
            QPointF ibr = d->_pixmap->mapToScene(d->_pixmap->boundingRect().bottomRight());

            return QRectF(itl, ibr);
        }

        QPointF ImageViewer::mapWidgetFromRulerPos(const QPointF p)
        {
            return mapFromScene(mapSceneFromRulerPos(p));
        }

        QPointF ImageViewer::mapWidgetToRulerPos(const QPointF p)
        {
            OI_F(ImageViewer);

            return mapSceneToRulerPos(mapToScene(p.x(), p.y()));
        }

        QPointF ImageViewer::mapSceneFromRulerPos(const QPointF p)
        {
            OI_F(ImageViewer);

            QPointF stl = mapToScene(0, 0);
            QPointF sbr = mapToScene(viewport()->width(), viewport()->height());

            QPointF rtl = QPointF(d->_hAxis->start(), d->_vAxis->end());
            QPointF rbr = QPointF(d->_hAxis->end(), d->_vAxis->start());

            double ratioX = ( sbr.x() - stl.x() ) / ( rbr.x() - rtl.x() );
            double ratioY = ( sbr.y() - stl.y() ) / ( rbr.y() - rtl.y() );

            return stl + QPointF(( p.x() - rtl.x() ) * ratioX, ( p.y() - rtl.y() ) * ratioY);
        }

        QPointF ImageViewer::mapSceneToRulerPos(const QPointF p)
        {
            OI_F(ImageViewer);

            QPointF stl = mapToScene(0, 0);
            QPointF sbr = mapToScene(viewport()->width(), viewport()->height());

            QPointF rtl = QPointF(d->_hAxis->start(), d->_vAxis->end());
            QPointF rbr = QPointF(d->_hAxis->end(), d->_vAxis->start());

            double ratioX = ( rbr.x() - rtl.x() ) / ( sbr.x() - stl.x() );
            double ratioY = ( rbr.y() - rtl.y() ) / ( sbr.y() - stl.y() );

            return rtl + QPointF(( p.x() - stl.x() ) * ratioX, ( p.y() - stl.y() ) * ratioY);
        }

        QPointF ImageViewer::mapImageFromRulerPos(const QPointF p)
        {
            auto scenePos = mapSceneFromRulerPos(p);
            return OI_IMPL()->_pixmap->mapFromScene(scenePos);
        }

        QPointF ImageViewer::mapImageToRulerPos(const QPointF p)
        {
            auto scenePos = OI_IMPL()->_pixmap->mapToScene(p);
            return mapSceneToRulerPos(scenePos);
        }

        void ImageViewer::display(const QImage& image, int start, int lines)
        {
            OI_F(ImageViewer);

            if(d->preview(image))
            {
                if(d->_3DLayer->isVisible())
                {
                    d->_raster->display(image, start, lines);
                }
            }

            d->updateAxis();
            
        }

        void ImageViewer::display(const Core::RangeMapPtr& rangeMap, int start, int lines)
        {
            OI_F(ImageViewer);

            if(d->preview(rangeMap))
            {
                if(d->_3DLayer->isVisible())
                {
                    d->_raster->display(rangeMap, start, lines);
                }
            }

            d->updateAxis();
        }

        void ImageViewer::display(const Core::PointMapPtr& pointMap, int start, int lines)
        {
            OI_F(ImageViewer);

            if(d->preview(pointMap))
            {
                if(d->_3DLayer->isVisible())
                {
                    d->display3D_Pointmap();
                }
            }

            d->updateAxis();
        }

        void ImageViewer::clearAll()
        {
            OI_F(ImageViewer);
            d->_pixmap->setPixmap(QImage());
            d->_rangeMap.clear();
            d->_pointMap.clear();
            d->_pointMat.release();
            d->_pointZAvg.clear();

            d->_raster->display(Core::RangeMapPtr());

            emit clearView();
        }

        void ImageViewer::blockContextMenuEvent()
        {
            OI_F(ImageViewer);

            d->_blockContextMenu = true;
        }

        bool ImageViewer::event(QEvent* event)
        {
            OI_F(ImageViewer);

            if(event->type() == QEvent::LayoutRequest)
            {
                d->relayout();
                d->updateAxis();
            }

            return QGraphicsView::event(event);
        }

        void ImageViewer::contextMenuEvent(QContextMenuEvent *event)
        {
            OI_F(ImageViewer);

            if (d->_blockContextMenu)
            {
                d->_blockContextMenu = false;
                return;
            }

            if(d->_3DLayer->isVisible())
            {
                return;
            }

            QMenu menu(this);

            auto action = menu.addAction(tr("缩放至合适"));
            action->setData(0);

            menu.addSeparator();

            action = menu.addAction(tr("水平翻转"));
            action->setData(1);
            action->setEnabled(d->_editable);

            action = menu.addAction(tr("垂直翻转"));
            action->setData(2);
            action->setEnabled(d->_editable);

            action = menu.addAction(tr("旋转数据"));
            action->setData(3);
            action->setEnabled(d->_editable);

            action = menu.addSeparator();

            action = menu.addAction(tr("裁切数据"));
            action->setData(4);
            action->setEnabled(d->_editable);

            action = menu.addAction(tr("清理视图"));
            action->setData(5);
            action->setEnabled(d->_editable);

            action = menu.exec(event->globalPos());

            if(action)
            {
                switch(action->data().toInt())
                {
                case 0:
                zoomFit();
                break;
                case 1:
                emit hFlip();
                break;
                case 2:
                emit vFlip();
                break;
                case 3:
                emit rotateData();
                break;
                case 4:
                emit cutData();
                break;
                case 5:
                clearAll();
                break;
                }
            }

            scene()->invalidate();
        }

        void ImageViewer::keyPressEvent(QKeyEvent *event)
        {
            OI_F(ImageViewer);

            switch(event->key())
            {
            case Qt::Key_PageUp:
            event->accept();
            return;
            case Qt::Key_PageDown:
            event->accept();
            return;
            case Qt::Key_HomePage:
            event->accept();
            return;
            case Qt::Key_Up:
            event->accept();
            return;
            case Qt::Key_Down:
            event->accept();
            return;
            case Qt::Key_Left:
            event->accept();
            return;
            case Qt::Key_Right:
            event->accept();
            return;
            case Qt::Key_Tab:
            event->accept();
            return;
            }

            QGraphicsView::keyPressEvent(event);
        }

        void ImageViewer::keyReleaseEvent(QKeyEvent *event)
        {
            OI_F(ImageViewer);

            switch(event->key())
            {
            case Qt::Key_PageUp:
            event->accept();
            return;
            case Qt::Key_PageDown:
            event->accept();
            return;
            case Qt::Key_HomePage:
            event->accept();
            return;
            case Qt::Key_Up:
            event->accept();
            return;
            case Qt::Key_Down:
            event->accept();
            return;
            case Qt::Key_Left:
            event->accept();
            return;
            case Qt::Key_Right:
            event->accept();
            return;
            case Qt::Key_Tab:
            event->accept();
            return;
            }

            QGraphicsView::keyReleaseEvent(event);
        }

        void ImageViewer::enterEvent(QEvent*)
        {
            OI_F(ImageViewer);

            if(d->_3DLayer->isVisible())
            {
                d->_raster->setFocus();
            }

            if(d->_timer.isActive())
            {
                d->_timer.stop();
            }
            else
            {
                if(d->_allowShow)
                {
                    d->_tContext->show();
                }
            }
        }

        void ImageViewer::leaveEvent(QEvent*)
        {
            if(!OI_IMPL()->_timer.isActive())
            {
                OI_IMPL()->_timer.start(500);
            }
        }

        void ImageViewer::mousePressEvent(QMouseEvent *event)
        {
            OI_F(ImageViewer);
            if(!d->_3DLayer->isVisible())
            {
                if(event->button() == Qt::MiddleButton)
                {
                    setDragMode(QGraphicsView::ScrollHandDrag);
                    QMouseEvent evt(event->type(), event->pos(), Qt::LeftButton, event->buttons(), event->modifiers());
                    QGraphicsView::mousePressEvent(&evt);
                    event->accept();
                    d->relayout();
                    d->updateAxis();
                    return;
                }
            }
            QGraphicsView::mousePressEvent(event);
        }

        void ImageViewer::mouseReleaseEvent(QMouseEvent *event)
        {
            OI_F(ImageViewer);
            if(!d->_3DLayer->isVisible())
            {
                if(event->button() == Qt::MiddleButton)
                {
                    QMouseEvent evt(event->type(), event->pos(), Qt::LeftButton, event->buttons(), event->modifiers());
                    QGraphicsView::mouseReleaseEvent(&evt);
                    event->accept();
                    setDragMode(QGraphicsView::RubberBandDrag);
                    d->relayout();
                    d->updateAxis();
                    return;
                }
            }
            QGraphicsView::mouseReleaseEvent(event);
        }

        void ImageViewer::mouseMoveEvent(QMouseEvent *event)
        {
            OI_F(ImageViewer);

            if(Core::OiGetTarget() == OiCreator)
            {
                if(d->_3DLayer->isVisible())
                {
                }
                else
                {
                }
            }

            QGraphicsView::mouseMoveEvent(event);

            if(dragMode() == QGraphicsView::ScrollHandDrag)
            {
                d->relayout();
                d->updateAxis();
            }

            QGraphicsView::mouseMoveEvent(event);
        }

        void ImageViewer::mouseDoubleClickEvent(QMouseEvent *event)
        {
            OI_F(ImageViewer);

            if(event->button() == Qt::MiddleButton)
            {
                zoomFit();
            }

            QGraphicsView::mouseDoubleClickEvent(event);

            d->relayout();
            d->updateAxis();
        }

        bool ImageViewer::focusNextPrevChild(bool next)
        {
            return false;
        }

        void ImageViewer::wheelEvent(QWheelEvent *event)
        {
            OI_F(ImageViewer);
            if(d->_3DLayer->isVisible() && !( event->modifiers() & Qt::CTRL ))
            {
                QGraphicsView::wheelEvent(event);
            }
            else
            {
                if(event->delta() > 0)
                {
                    if(transform().m11() < 1e+5)
                    {
                        scale(1.0 * 1.15, 1.0 * 1.15);
                        d->relayout();
                        d->updateAxis();
                    }
                }
                else
                {
                    if(transform().m11() > 1e-5)
                    {
                        scale(1.0 / 1.15, 1.0 / 1.15);
                        d->relayout();
                        d->updateAxis();
                    }
                }
            }
        }

        void ImageViewer::resizeEvent(QResizeEvent *event)
        {
            OI_F(ImageViewer);

            int w = viewport()->width();
            int h = viewport()->height();

            if(d->_3DLayer->isVisible())
            {
                d->_raster->resize(w, h);
            }

            QGraphicsView::resizeEvent(event);

            d->relayout(w, h);

            zoomFit();
        }

        void ImageViewer::showEvent(QShowEvent *event)
        {
            OI_F(ImageViewer);
            QGraphicsView::showEvent(event);
            d->relayout();
            d->updateAxis();
        }

        void ImageViewer::hideEvent(QHideEvent *event)
        {
            OI_F(ImageViewer);
            QGraphicsView::hideEvent(event);
            d->relayout();
            d->updateAxis();
        }

        void ImageViewer::paintEvent(QPaintEvent *event)
        {
            QGraphicsView::paintEvent(event);
        }


        void ImageViewer::dragEnterEvent(QDragEnterEvent* evt)
        {
            if (evt->mimeData()->hasUrls() && evt->mimeData()->urls().size() == 1)
            {
                evt->acceptProposedAction();
            }
        }

        void ImageViewer::dragLeaveEvent(QDragLeaveEvent* evt)
        {
            QGraphicsView::dragLeaveEvent(evt);
        }

        void ImageViewer::dropEvent(QDropEvent* evt)
        {
            auto urls = evt->mimeData()->urls();
            if(urls.size() == 1)
            {
                bool accepted = false;

                QString filename = urls.front().toLocalFile();

                if(!accepted)
                {
                    if(filename.endsWith(".bmp") || filename.endsWith(".png") || filename.endsWith(".gif") || filename.endsWith(".jpg") || filename.endsWith(".tif") || filename.endsWith(".jpeg") || filename.endsWith(".tiff"))
                    {
                        display(QImage(filename));
                    }
                    else if(filename.endsWith(".apm") || filename.endsWith(".arm") || filename.endsWith(".awm") || filename.endsWith(".aqm") || filename.endsWith(".mat") || filename.endsWith(".rmf"))
                    {
                        display(Core::RangeMap::create(filename));
                    }
                    else if(filename.endsWith(".dis") || filename.endsWith(".pmf"))
                    {
                        display(Core::PointMap::create(filename));
                    }
                    else
                    {
                        emit loadFile(filename);
                    }
                }
            }
        }

        void ImageViewer::onActionTriggered()
        {
            OI_F(ImageViewer);

            QAction* action = qobject_cast<QAction*>( sender() );

            if(!action)
            {
                return;
            }

            switch(action->data().toInt())
            {
            case 0:
            zoomFit();
            break;
            case 3:
            {
                action->setData(4);
                action->setIcon(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/2d.png"));
                action->setText(ImageViewer::tr("二维模式"));

                setAutoFillBackground(false);

                d->_hAxis->hide();
                d->_vAxis->hide();
                d->_2DLayer->hide();
                d->_3DLayer->show();
                d->_raster->setFocus();

                d->_raster->resize(viewport()->width(), viewport()->height());

                if(!d->_pointMap.isNull())
                {
                    d->display3D_Pointmap();
                }
                else if(!d->_rangeMap.isNull())
                {
                    d->_raster->display(d->_rangeMap);
                }
                else
                {
                    d->_raster->display(d->_pixmap->pixmap());
                }
              
                zoomFit();

                if(d->_raster->isParameterPanelVisible())
                {
                    d->_pulse.start();
                }
            }
            break;
            case 4:
            {
                action->setData(3);
                action->setIcon(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/3d.png"));
                action->setText(ImageViewer::tr("三维模式"));

                setAutoFillBackground(true);

                if(d->_raster->isParameterPanelVisible())
                {
                    d->_pulse.stop();
                }

                d->_raster->display(Core::RangeMapPtr());

                d->_3DLayer->hide();
                d->_2DLayer->show();
                d->_hAxis->show();
                d->_vAxis->show();

                zoomFit();
            }
            break;
            case 5:
            {
                if (d->_2DLayer->isVisible() && d->_colorMode)
                {
                    action->setData(6);
                    action->setIcon(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/color.png"));
                    action->setText(ImageViewer::tr("着色图"));
                    d->_colorMode = false;

                    d->updateImage();
                }
            }
            break;
            case 6:
            {
                if(d->_2DLayer->isVisible() && !d->_colorMode)
                {
                    action->setData(5);
                    action->setIcon(QIcon(":/resources/"+PLScanner::instance()->resourceName+"/mono.png"));
                    action->setText(ImageViewer::tr("黑白图"));
                    d->_colorMode = true;

                    d->updateImage();
                }
            }
            break;
            case 10:
            d->snapshot("MAT");
            break;
            case 11:
            d->snapshot("XYZ");
            break;
            case 12:
            d->snapshot("ASC");
            break;
            case 13:
            d->snapshot("STL");
            break;
            case 14:
            d->snapshot("PLY");
            break;
            case 15:
            d->snapshot("IGS");
            break;
            }
        }

        void ImageViewer::drawForeground(QPainter *painter, const QRectF &rect)
        {
            OI_F(ImageViewer);

            if(d->_displayAction->isChecked() && !d->_3DLayer->isVisible())
            {
                if(!d->_rubberRect.isNull())
                {
                    painter->fillRect(d->_rubberRect, QColor("#AA3399FF"));
                }
            }

            QGraphicsView::drawForeground(painter, rect);
        }

        void ImageViewer::drawBackground(QPainter *painter, const QRectF &rect)
        {
            QGraphicsView::drawBackground(painter, rect);
        }

        void ImageViewer::invalidate()
        {
            if(scene())
            {
                scene()->invalidate();
            }
        }

        void ImageViewer::onTimeout()
        {
            if(OI_IMPL()->_allowHide)
            {
                OI_IMPL()->_tContext->hide();
            }
        }

        void ImageViewer::show3DLayer()
        {
            if(OI_IMPL()->_displayAction->data().toInt() == 3)
            {
                OI_IMPL()->_displayAction->trigger();
            }
        }

        void ImageViewer::show2DLayer()
        {
            if(OI_IMPL()->_displayAction->data().toInt() == 4)
            {
                OI_IMPL()->_displayAction->trigger();
            }
        }

        ImageViewer* ImageViewer::OiImageViewer()
        {
            if(Core::OiGetMainWindow()->property("ImageViewer").isValid())
            {
                return qobject_cast<ImageViewer*>( Core::OiGetMainWindow()->property("ImageViewer").value<QWidget*>() );

            }
            else
            {
                return nullptr;
            }
        }


    }
}